package opencm.cli;

import opencm.Agendamento;
import opencm.Consulta;
import opencm.ErroAgendamento;
import opencm.Especialidade;
import opencm.Main;
import opencm.Medico;
import opencm.Paciente;
import opencm.RepositorioConsulta;
import opencm.RepositorioMedico;
import opencm.RepositorioPaciente;
import opencm.ServicoAgendamento;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class MarcarConsulta extends Acao {

	private RepositorioPaciente pacientes;
	private ServicoAgendamento agendador;
	private RepositorioMedico medicos;
	private RepositorioConsulta consultas;

	public MarcarConsulta() {
		this.pacientes = Main.repositorioPaciente;
		this.agendador = new ServicoAgendamento();
		this.consultas = Main.repositorioConsulta;
	}
	
	@Override
	public void executar() {
		if (this.pacientes.contar() == 0) {
			out.println("Nenhum Paciente. Você deve cadastrar o paciente primeiro.");
			System.exit(1);
		}
		
		Consulta consulta = null;
		
		while(true) {
			try {
				consulta = new Consulta(readPaciente(), readTipo());
				
				Agendamento.Tipo tipo = selecionarUm("Tipo de Agendamento", Agendamento.Tipo.toList());
				switch (tipo) {
				case MEDICO:
					agendarPorMedico(consulta, readMedico(), new DateTime());
					break;
				case ESPECIALIDADE:
					agendarPorEspecialidade(consulta, readEspecialidade(), new DateTime());
					break;
				case ESPERA_ESPECIALIDADE:
					consulta.agendar(Agendamento.criarEsperaPorEspecialidade(readEspecialidade(), readExpiraEm()));
					break;
				case ESPERA_MEDICO:
					consulta.agendar(Agendamento.criarEsperaPorMedico(readMedico(), readExpiraEm()));
					break;
				default:
					assert false;
					break;
				}
				break;
			} catch (IllegalArgumentException e) {
				out.println(e.getMessage());
			}
		}
		
		this.consultas.adicionar(consulta);
		
		out.printf("Consulta adicionada com sucesso!%n");
		out.printf("%n");
		out.printf("%20s: %s%n", "Paciente", consulta.getPaciente());
		out.printf("%20s: %s%n", "Médico", consulta.getAgendamento().getMedico().toString());
		out.printf("%20s: %s%n", "Especialidade", consulta.getAgendamento().getMedico().getEspecialidade().toString());
		out.printf("%20s: %s%n", "Em", consulta.getAgendamento().getInicio().toString());
		out.printf("%20s: %s%n", "Duração", consulta.getAgendamento().getDuracao().toString());
	}
	
	private LocalDate readExpiraEm() {
		DateTimeFormatter fmt = DateTimeFormat.forPattern("dd/MM/yyy");
		while(true) {
			try {
				return fmt.parseDateTime(readLine("Expira em", true)).toLocalDate();
			} catch (IllegalArgumentException e) {
				out.println("Data inválida.");
			}
		}	
	}

	private void agendarPorMedico(Consulta consulta, Medico medico, DateTime apartirDe) {
		try {
			consulta.agendar(this.agendador.criarAgendamento(consulta, medico, apartirDe));
		} catch (ErroAgendamento e) {
			agendarPorMedico(consulta, medico, apartirDe.plusDays(1));
		}
	}

	private void agendarPorEspecialidade(Consulta consulta, Especialidade especialidade, DateTime apartirDe) {
		try {
			consulta.agendar(this.agendador.criarAgendamento(consulta, especialidade, apartirDe));
		} catch (ErroAgendamento e) {
			agendarPorEspecialidade(consulta, especialidade, apartirDe.plusDays(1));
		}
	}
	
	@Override
	public String toString() {
		return "Marcar Consulta";
	}
	
	private Medico readMedico() {
		return this.selecionarUm("Médico", medicos.pesquisar());
	}
	
	private Consulta.Tipo readTipo() {
		return this.selecionarUm("Tipo de Consulta", Consulta.Tipo.toList());
	}
	
	private Paciente readPaciente() {
		return this.selecionarUm("Paciente", pacientes.pesquisar());
	}
	
	private Especialidade readEspecialidade() {
		return selecionarUm("Especialidade", Especialidade.toList());
	}
	
}
